#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

// 定义信号量操作
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

// P操作 - 等待信号量
void sem_wait(int sem_id, int sem_num) {
    struct sembuf sb = {sem_num, -1, 0};
    semop(sem_id, &sb, 1);
}

// V操作 - 释放信号量
void sem_signal(int sem_id, int sem_num) {
    struct sembuf sb = {sem_num, 1, 0};
    semop(sem_id, &sb, 1);
}

// 共享内存数据结构
struct SharedData {
    int round;          // 当前轮次
    int player_a_choice; // 选手A的选择
    int player_b_choice; // 选手B的选择
    int result;         // 比赛结果
    int ready;          // 数据就绪标志
};

// 判断出拳结果
int result_announce(int a, int b) {
    if (a == b) return 0;                   // 平局
    if ((a == 0 && b == 1) ||               // 石头胜剪刀
        (a == 1 && b == 2) ||               // 剪刀胜布
        (a == 2 && b == 0))                 // 布胜石头
        return -1;                          // A胜
    return 1;                               // B胜
}

// 显示比赛结果
void displayResults(int *results, int rounds) {
    int wins_A = 0, wins_B = 0, draws = 0;
    
    for (int i = 0; i < rounds; i++) {
        switch(results[i]) {
            case -1: wins_A++; break;
            case 0: draws++; break;
            case 1: wins_B++; break;
        }
    }
    
    printf("\n=== 最终统计 ===\n");
    printf("A胜: %d次\nB胜: %d次\n平局: %d次\n", wins_A, wins_B, draws);
    printf("最终胜利者: %s\n", wins_A > wins_B ? "A" : wins_B > wins_A ? "B" : "平局");
}

int main() {
    int rounds;
    int *result_list;
    int shm_id;
    int sem_id;
    struct SharedData *shared_data;
    
    srand(time(NULL));  // 初始化随机数种子
    
    printf("请输入比赛轮数: ");
    scanf("%d", &rounds);
    
    // 分配结果数组内存
    result_list = malloc(rounds * sizeof(int));
    
    // 创建共享内存
    shm_id = shmget(IPC_PRIVATE, sizeof(struct SharedData), IPC_CREAT | 0666);
    if (shm_id == -1) {
        perror("共享内存创建失败");
        exit(EXIT_FAILURE);
    }
    
    // 附加共享内存
    shared_data = (struct SharedData *)shmat(shm_id, NULL, 0);
    if (shared_data == (void *)-1) {
        perror("共享内存附加失败");
        exit(EXIT_FAILURE);
    }
    
    // 创建信号量集（3个信号量）
    // 0: 用于保护共享内存
    // 1: 用于选手A同步
    // 2: 用于选手B同步
    sem_id = semget(IPC_PRIVATE, 3, IPC_CREAT | 0666);
    if (sem_id == -1) {
        perror("信号量创建失败");
        exit(EXIT_FAILURE);
    }
    
    // 初始化信号量
    union semun arg;
    unsigned short values[3] = {1, 0, 0};  // 互斥锁初始为1，其他为0
    arg.array = values;
    if (semctl(sem_id, 0, SETALL, arg) == -1) {
        perror("信号量初始化失败");
        exit(EXIT_FAILURE);
    }
    
    // 初始化共享内存
    shared_data->round = 0;
    shared_data->ready = 0;
    
    for (int i = 0; i < rounds; i++) {
        // 创建选手A进程
        if (fork() == 0) {
            srand(time(NULL) ^ getpid());   // 设置进程特有随机种子
            
            // 等待轮到选手A
            sem_wait(sem_id, 1);
            
            // 保护共享内存访问
            sem_wait(sem_id, 0);
            
            // 生成并保存选手A的选择
            shared_data->player_a_choice = rand() % 3;
            
            // 释放共享内存锁
            sem_signal(sem_id, 0);
            
            // 通知选手B
            sem_signal(sem_id, 2);
            
            exit(0);
        }
        
        // 创建选手B进程
        if (fork() == 0) {
            srand(time(NULL) ^ getpid());   // 设置进程特有随机种子
            
            // 等待轮到选手B
            sem_wait(sem_id, 2);
            
            // 保护共享内存访问
            sem_wait(sem_id, 0);
            
            // 生成并保存选手B的选择
            shared_data->player_b_choice = rand() % 3;
            
            // 标记数据就绪
            shared_data->ready = 1;
            
            // 释放共享内存锁
            sem_signal(sem_id, 0);
            
            exit(0);
        }
        
        // 父进程 - 启动新一轮比赛
        sem_wait(sem_id, 0);
        shared_data->round = i + 1;
        shared_data->ready = 0;
        sem_signal(sem_id, 0);
        
        // 通知选手A开始
        sem_signal(sem_id, 1);
        
        // 等待数据就绪
        while (1) {
            sem_wait(sem_id, 0);
            if (shared_data->ready) {
                break;
            }
            sem_signal(sem_id, 0);
            usleep(1000);  // 短暂休眠，避免忙等待
        }
        
        // 读取结果并判断胜负
        int a_choice = shared_data->player_a_choice;
        int b_choice = shared_data->player_b_choice;
        result_list[i] = result_announce(a_choice, b_choice);
        
        // 重置就绪标志
        shared_data->ready = 0;
        sem_signal(sem_id, 0);
        
        // 显示本轮结果
        char *choices[] = {"石头", "剪刀", "布"};
        printf("轮次%d: A出%s, B出%s -> %s\n", 
               i+1, 
               choices[a_choice], 
               choices[b_choice],
               result_list[i] == -1 ? "A胜" : result_list[i] == 0 ? "平局" : "B胜");
        
        // 等待子进程结束
        wait(NULL);
        wait(NULL);
    }
    
    // 显示最终结果
    displayResults(result_list, rounds);
    
    // 清理资源
    free(result_list);
    shmdt(shared_data);
    shmctl(shm_id, IPC_RMID, NULL);
    semctl(sem_id, 0, IPC_RMID);
    
    return 0;
}